/*
 * Implementacion de Automata Finito para reconocer numeros enteros y decimales.
 * 
 * Basado en el proyecto descrito en: 
 * Louden, K. (1997). Compiler Construction: Principles and Practice. Boston: PWS.
 * 
 * @ Project: Compiler
 */

package cml.scanning.automata;

import cml.TokenType;

public class DecAutomaton extends Automaton {

	private enum StateType{START, END_INT, IN_DEC, END_DEC, DONE, ERROR};
	private StateType state;
	private StateType last_state;
	
	/*
	 * Constructor de la clase.
	 */
	public DecAutomaton(){		

		this.state = StateType.START;
		this.last_state = this.state;

		/*
		 *		 	.		digit	other
		 *	START	NO_BRA	END_INT	NO_BRA
		 *	END_INT	IN_DEC	END_INT [DONE]
		 *	IN_DEC	[DONE]	END_DEC	[DONE]
		 *	END_DEC	[DONE]	END_DEC	[DONE]
		 *	DONE	ERROR	ERROR	ERROR
		 *	ERROR	NO_BRA	NO_BRA	NO_BRA
		 *
		 *	Los corchetes [] significa que se debe hacer backtrack 
		 *  al apuntador de la entrada de datos.
		 *  
		 *  NO_BRA = NO_BRANCH
		 *
		 */

		this.successorFunc = new Branch[6][3];
		this.successorFunc[0][0] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[0][1] = new Branch(StateType.END_INT.ordinal(), true);
		this.successorFunc[0][2] = new Branch(this.NO_BRANCH, false);
		
		this.successorFunc[1][0] = new Branch(StateType.IN_DEC.ordinal(), true);
		this.successorFunc[1][1] = new Branch(StateType.END_INT.ordinal(), true);
		this.successorFunc[1][2] = new Branch(StateType.DONE.ordinal(), false);
		
		this.successorFunc[2][0] = new Branch(StateType.DONE.ordinal(), false);
		this.successorFunc[2][1] = new Branch(StateType.END_DEC.ordinal(), true);
		this.successorFunc[2][2] = new Branch(StateType.DONE.ordinal(), false);

		this.successorFunc[3][0] = new Branch(StateType.DONE.ordinal(), false);
		this.successorFunc[3][1] = new Branch(StateType.END_DEC.ordinal(), true);
		this.successorFunc[3][2] = new Branch(StateType.DONE.ordinal(), false);
		
		this.successorFunc[4][0] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[4][1] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[4][2] = new Branch(StateType.ERROR.ordinal(), false);
		
		this.successorFunc[5][0] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[5][1] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[5][2] = new Branch(this.NO_BRANCH, false);
		
		this.acceptState = new boolean[StateType.values().length];		
		this.acceptState[StateType.START.ordinal()] = false;
		this.acceptState[StateType.END_INT.ordinal()] = false;
		this.acceptState[StateType.IN_DEC.ordinal()] = false;
		this.acceptState[StateType.END_DEC.ordinal()] = false;		
		this.acceptState[StateType.DONE.ordinal()] = true;
		this.acceptState[StateType.ERROR.ordinal()] = false;

		this.tokenString = "";
		this.backtrack = false;
	}
	
	/*
	 * Ejecuta la siguiente transicion (si la hay) leyendo el caracter pasado 
	 * como parametro.
	 * 
	 * @param	char	el siguiente caracter a leer
	 * @return			<code>true</code> si ejecuto una transicion; 
	 * 					<code>false</code> en caso contrario
	 */
	@Override
	public boolean nextStep(char c) {

		int idx2 = (c == '.') ? 0 : (Character.isDigit(c)) ? 1 : 2;
		
		Branch b = this.successorFunc[state.ordinal()][idx2];

		if(b.getNewState() != this.NO_BRANCH)
			this.state = StateType.values()[b.getNewState()];
		else
			return false;

		if(b.save())
			tokenString += c;

		if(this.state == StateType.ERROR){

			this.currentToken = TokenType.ERROR;
			return false;
		}

		if(this.acceptState[this.state.ordinal()]){

			if(this.last_state == StateType.END_INT)
				this.currentToken = TokenType.NUMINT;
			else
				this.currentToken = TokenType.NUMDEC;

			this.backtrack = true;
		}

		this.last_state = this.state;
		return true;
	}
	
	/*
	 * Reinicia al automata.
	 */
	@Override
	public void restart() {
		
		this.state = StateType.START;
		this.currentToken = null;
		this.tokenString = "";
	}
	
	/*
	 * Indica si el estado actual es el inicial.
	 * 
	 * @return	<code>true</code> si es el estado inicial;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean inStartState() {

		return this.state == StateType.START;
	}
	
	/*
	 * Indica si el estado actual es de aceptacion.
	 * 
	 * @return	<code>true</code> si es estado de aceptacion;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean isDone() {
		
		return this.state == StateType.DONE;
	}
}